using System;
using System.Drawing;
using Motorway_Simulator.Components;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading;

namespace Motorway_Simulator.Components
{
    /// <summary>
    /// Entrance road unit component
    /// </summary>
    [Serializable()]
    public class Entrance : RoadUnit, ISerializable
    {
        /// <summary>
        /// Holds the flow rate of vehicles entering motorway
        /// </summary>
        private int flow;

        private int count;
        /// <summary>
        /// 
        /// </summary>
        private bool stopRequested;

        /// <summary>
        /// Gets or sets a value indicating whether [stop has been requested].
        /// </summary>
        /// <value><c>true</c> if [stop requested]; otherwise, <c>false</c>.</value>
        public bool StopRequested
        {
            get { return stopRequested; }
            set { stopRequested = value; }
        }
        
        
        /// <summary>
        /// Unit icon to display entrance image on the road unit list view
        /// </summary>
        public readonly static Bitmap unitIcon = Motorway_Simulator.Properties.Resources.Entrance;
        /// <summary>
        /// To hold vehicle queue
        /// </summary>
        private LinkedList<Vehicle> vehicleQueue;
        /// <summary>
        /// Indicatas if entrance has a traffic light
        /// </summary>
        private bool hasTrafficLight;
        /// <summary>
        /// Traffic light object
        /// It is null if hasTrafficLight is false
        /// </summary>
        private TrafficLight trafficLight;

        /// <summary>
        /// The controller for the Entrance
        /// </summary>
        private Simulator.SimulationController controller;

        public Simulator.SimulationController Controller
        {
            set { controller = value; }
        }
        /// <summary>
        /// Constructor to create an entrance object
        /// </summary>
        /// <param name="initX">Position X</param>
        /// <param name="initY">Postion Y</param>
        /// <param name="direction">Direction</param>
        /// <param name="zoom">Zoom level</param>
        public Entrance(int initX, int initY, Direction direction, float zoom)
            : base()
        {
            // Default settings
            count = 0;
            flow = 5;
            controller = null;
            position.X = initX;
            position.Y = initY;
            this.direction = direction;
            RoadImage = Motorway_Simulator.Properties.Resources.Entrance;
            ImageZoom = zoom;
            RoadImage = RotateImageClockWise(image);
            vehicleQueue = new LinkedList<Vehicle>();
            hasTrafficLight = false;
            trafficLight = null;
            stopRequested = false;
            // Populate the queue with vehicles
            PopulateVehicleQueue();

        }

        
        /// <summary>
        /// Populates vehicle into a queue. Default is always 10
        /// in the vehicle queue
        /// </summary>
        private void PopulateVehicleQueue()
        {
            if (flow <= count)
            {
                vehicleQueue.AddLast(CreateVehicle());
                count = 0;
            }
        }

        /// <summary>
        /// Entrance release a vehicle and remove from front of the queue
        /// It replaces another random vehicle to the end of the queue if not enought space it dosent release the vehicle
        /// </summary>
        private void ReleaseFirstVehicle()
        {
            if (vehicleQueue.Count != 0)
            { 
                if (controller.getNeighbours(vehicleQueue.First.Value, vehicleQueue.First.Value.Length).Count <= 0)
                {
                    vehicleQueue.First.Value.IsIndicatingRight = true;
                    controller.AddDriver(GetDriver(vehicleQueue.First.Value));

                    vehicleQueue.RemoveFirst();
                }
            }
        }

        private Driver GetDriver(Vehicle vehicle)
        {
            Random generator = new Random();
            // Randomly chooses a new vehicle
            int randVehicleType = generator.Next(0, 2);
            Driver driver;
            // Creates a new vehicle depending on random number
            switch (randVehicleType)
            {
                case 0:
                         driver = new PassiveDriver(vehicle,this,controller);
                    return driver;
                case 1:
                     driver = new AggressiveDriver(vehicle, this, controller);
                    return driver;
                case 2:
                    driver = new ActiveDriver(vehicle, this, controller);
                    return driver;
                
                default:
                    driver = new PassiveDriver(vehicle, this, controller);
                    return driver;
            }
        }


        /// <summary>
        /// Draws image on the grid
        /// </summary>
        /// <param name="g">Graphics object</param>
        public override void Draw(Graphics g)
        {
            g.DrawImage(image, position.X, position.Y, ImageWidth, ImageHeight);
        }

        /// <summary>
        /// Accessor method to get/set Flow
        /// </summary>
        public int Flow
        {
            set { flow = value; }
            get { return flow; }
        }


        /// <summary>
        /// Generates a random vehicle types
        /// </summary>
        /// <returns></returns>
        private Vehicle CreateVehicle()
        {
            Random generator = new Random();
            // Randomly chooses a new vehicle
            int randVehicleType = generator.Next(0, 5);

            // Creates a new vehicle depending on random number
            switch (randVehicleType)
            {
                case 0:
                    Car car = new Car(position,this);
                    return car;
                case 1:
                    Van van = new Van(position,this);
                    return van;
                case 2:
                    Bus bus = new Bus(position,this);
                    return bus;
                case 3:
                    Truck truck = new Truck(position,this);
                    return truck;
                case 4:
                    OverSizeTruck oTruck = new OverSizeTruck(position,this);
                    return oTruck;
                case 5:
                    MotorBike motorBike = new MotorBike(position,this);
                    return motorBike;
                default:
                    Car car1 = new Car(position,this);
                    return car1;
            }
        }





        /// <summary>
        /// Gets or sets a value indicating whether [traffic light].
        /// </summary>
        /// <value><c>true</c> if [traffic light]; otherwise, <c>false</c>.</value>
        public bool TrafficLight
        {
            get { return hasTrafficLight; }
            set
            {
                hasTrafficLight = value;
                if (hasTrafficLight)
                {
                    if (trafficLight == null)
                    {
                        // Creates traffic light it doesn't already exists
                        trafficLight = new TrafficLight();
                    }

                }
                else
                {   // Removes traffic light
                    trafficLight = null;
                }
            }
        }

        /// <summary>
        /// Gets the next point
        /// </summary>
        /// <param name="position">Position</param>
        /// <param name="speed">Speed</param>
        /// <returns></returns>
        public override Point getNextPosition(Point position, int speed)
         {
            int y = position.Y;
            int x = position.X;
            int newX = 0;
            int newY = 0;
            if (direction == Direction.NORTH)
            {
                newX = x + (speed /5);
                newY = y - (speed / 5);
            }
            if (direction == Direction.SOUTH)
            {
                newX = x - (speed /5);
                newY = y + (speed /5);
            }
            if (direction == Direction.EAST)
            {
                newX = x + (speed / 5);
                newY = y + (speed / 5);
            }
            if (direction == Direction.WEST)
            {
                newX = x - (speed / 5);
                newY = y - (speed / 5);
            }

            return new Point(newX, newY);
        }

        #region ISerializable Members
        /// <summary>
        /// Add values to be serialized
        /// </summary>
        /// <param name="info">Info</param>
        /// <param name="context">Context</param>
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("VehicleQueue", vehicleQueue);
            info.AddValue("TrafficLight", trafficLight);
            info.AddValue("HasTrafficLight", hasTrafficLight);
            info.AddValue("Flow", flow);
            info.AddValue("Position", position);
            info.AddValue("Image", image);
            info.AddValue("ImageWidth", ImageWidth);
            info.AddValue("ImageHeight", ImageHeight);
            info.AddValue("RoadImage", roadImage);
            info.AddValue("ImageZoom", imageZoom);
            info.AddValue("Sign", sign);
            info.AddValue("HasSign", hasSign);
            info.AddValue("Direction", direction);
        }

        #endregion

        /// <summary>
        /// Is called to set values read from file
        /// </summary>
        /// <param name="info">Info</param>
        /// <param name="ctxt">ctxt</param>
        public Entrance(SerializationInfo info, StreamingContext ctxt)
        {
            vehicleQueue = (LinkedList<Vehicle>)info.GetValue("VehicleQueue", typeof(LinkedList<Vehicle>));
            trafficLight = (TrafficLight)info.GetValue("TrafficLight", typeof(TrafficLight));
            hasTrafficLight = (bool)info.GetValue("HasTrafficLight", typeof(bool));
            flow = (int)info.GetValue("Flow", typeof(int));
            position = (Point)info.GetValue("Position", typeof(Point));
            image = (Bitmap)info.GetValue("Image", typeof(Bitmap));
            ImageWidth = (float)info.GetValue("ImageWidth", typeof(float));
            ImageHeight = (float)info.GetValue("ImageHeight", typeof(float));
            roadImage = (Bitmap)info.GetValue("RoadImage", typeof(Bitmap));
            imageZoom = (float)info.GetValue("ImageZoom", typeof(float));
            sign = (Sign)info.GetValue("Sign", typeof(Sign));
            hasSign = (bool)info.GetValue("HasSign", typeof(bool));
            direction = (Direction)info.GetValue("Direction", typeof(Direction));
            //Get the values from info and assign them to the appropriate properties
        }

        /// <summary>
        /// Runs this instance.
        /// </summary>
         public void Run()
         {
            if (controller != null)
            {
                while (!stopRequested)
                {
                    count++;
                    PopulateVehicleQueue();
                    ReleaseFirstVehicle();
                    Thread.Sleep(2000);

                }
            }
        }

         internal void Reset()
         {
             // herers
         }

         /// <summary>
         /// Determines whether [is not in current road unit] [the specified vehicle].
         /// </summary>
         /// <param name="vehicle">The vehicle.</param>
         /// <returns>
         /// 	<c>true</c> if [is not in current road unit] [the specified vehicle]; otherwise, <c>false</c>.
         /// </returns>
         public override bool IsNotInCurrentRoadUnit(Vehicle vehicle)
         {
             switch (this.Direction1)
             {
                 case Direction.NORTH:
                     if (Position.X + ImageWidth <= vehicle.CurrentPoint.X)
                     {
                         return true;
                     }
                     break;

                 case Direction.SOUTH:
                     if (Position.X + ImageWidth >= vehicle.CurrentPoint.X)
                     {
                         return true;
                     }
                     break;
                 case Direction.WEST:

                     if (Position.Y + ImageHeight <= vehicle.CurrentPoint.Y)
                     {
                         return true;
                     }
                     break;
                 case Direction.EAST:
                     if (Position.Y+ ImageHeight <= vehicle.CurrentPoint.Y)
                     {
                         return true;
                     }
                     break;

             }
             return false;

         }
    }
}